package com.yf.exam.modules.user.cert.utils;

import com.yf.exam.ability.output.config.OutputConfig;
import com.yf.exam.modules.cert.entity.Cert;
import com.yf.exam.modules.sys.user.dto.SysUserDTO;
import org.apache.poi.xslf.usermodel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 证书生成服务
 */
@Service
public class CertGenerateUtils {

    // 常量定义
    private static final int PDF_CONVERSION_TIMEOUT_SECONDS = 30;
    private static final String DEFAULT_TEMPLATE_NAME = "template.pptx";
    private static final String DATE_FORMAT_PATTERN = "yyyy/MM/dd";
    private static final String CERT_DATE_FORMAT_PATTERN = "yyyy年MM月dd日";
    private static final String UPLOAD_FILE_PREFIX = "/upload/file/";
    private static final String USER_NAME_PLACEHOLDER = "{{USER_NAME}}";
    private static final String CERT_TITLE_PLACEHOLDER = "{{CERT_TITLE}}";
    private static final String CERT_CODE_PLACEHOLDER = "{{CERT_CODE}}";
    private static final String ISSUE_DATE_PLACEHOLDER = "{{ISSUE_DATE}}";

    private static final Logger log = LoggerFactory.getLogger(CertGenerateUtils.class);
    
    // 文件缓存，避免重复转换相同的文件
    private final Map<String, String> fileCache = new ConcurrentHashMap<>();

    @Value("${cert.template.path:./certs/templates/}")
    private String templatePath;

    // 注入证书输出配置
    @Autowired
    private OutputConfig outputConfig;

    /**
     * 生成证书（同步版本）
     * @param cert 证书模板信息
     * @param user 用户信息
     * @return 生成的PDF文件URL
     */
    public String generateCertificate(Cert cert, SysUserDTO user) {
        // 输入验证
        validateInputParameters(cert, user);
        
        // 检查缓存
        String cacheKey = generateCacheKey(cert, user);
        String cachedResult = fileCache.get(cacheKey);
        if (cachedResult != null && new File(cachedResult).exists()) {
            log.info("从缓存返回证书: {}", cachedResult);
            return cachedResult;
        }
        try {
            // 生成日期路径
            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT_PATTERN);
            String datePath = dateFormat.format(new Date());
            
            // 使用配置的证书输出目录 + 日期路径
            String outputPath = outputConfig.getDir() + datePath + "/";
            
            // 确保输出目录存在
            File outputDir = new File(outputPath);
            if (!outputDir.exists()) {
                outputDir.mkdirs();
            }

            // 构建文件名（只使用时间戳，不包含用户ID和证书ID）
            String fileName = String.valueOf(System.currentTimeMillis());
            String pptxOutputPath = outputPath + fileName + ".pptx";
            String pdfOutputPath = outputPath + fileName + ".pdf";

            // 简化的模板文件路径处理
            String templateFile = resolveTemplatePath(cert.getBackLogo());
            
            log.info("使用模板文件: {}", templateFile);
            
            // 验证模板文件是否存在
            if (!new File(templateFile).exists()) {
                throw new RuntimeException("模板文件不存在: " + templateFile);
            }
            
            // 准备替换文本
            Map<String, String> replacements = prepareReplacements(cert, user);

            // 处理PPTX文件
            processPPTX(templateFile, pptxOutputPath, replacements);

            // 转换为PDF
            convertToPDF(pptxOutputPath, pdfOutputPath);
            
            // PDF转换成功后，删除临时的PPTX文件
            try {
                File pptxFile = new File(pptxOutputPath);
                if (pptxFile.exists() && pptxFile.delete()) {
                    log.info("临时PPTX文件已删除: {}", pptxOutputPath);
                } else {
                    log.warn("删除临时PPTX文件失败: {}", pptxOutputPath);
                }
            } catch (Exception e) {
                log.warn("删除临时PPTX文件时发生异常: {}, 错误: {}", pptxOutputPath, e.getMessage());
            }

            // 返回完整的HTTP URL，包含日期路径
            // 注意：证书URL需要使用/upload/file/前缀以便UploadServiceImpl正确处理
            // 参照证书模板backLogo的URL格式，确保生成的URL可以在浏览器中直接下载
            String certUrl = outputConfig.getUrl() + datePath + "/" + fileName + ".pdf";
            
            // 缓存结果
            fileCache.put(cacheKey, certUrl);
            
            log.info("证书生成成功，用户: {}, 证书URL: {}", user.getRealName(), certUrl);
            log.info("证书物理文件路径: {}", pdfOutputPath);
            log.info("证书URL格式参照模板逻辑，可在浏览器中直接下载: {}", certUrl);
            
            return certUrl;

        } catch (Exception e) {
            log.error("生成证书过程中发生错误: {}", e.getMessage(), e);
            throw new RuntimeException("生成证书失败: " + e.getMessage(), e);
        }
    }

    /**
     * 解析模板文件路径（简化版本）
     * @param backLogoPath 证书模板的backLogo路径
     * @return 实际的模板文件路径
     */
    private String resolveTemplatePath(String backLogoPath) {
        String defaultTemplate = templatePath + DEFAULT_TEMPLATE_NAME;
        
        if (StringUtils.isEmpty(backLogoPath) || backLogoPath.matches("\\d+")) {
            return defaultTemplate;
        }
        
        if (backLogoPath.startsWith("http") && backLogoPath.contains(UPLOAD_FILE_PREFIX)) {
            String relativePath = extractRelativePath(backLogoPath);
            if (relativePath.toLowerCase().endsWith(".pptx")) {
                return "./upload/" + relativePath;
            }
            log.warn("无法解析HTTP URL: {}, 使用默认模板", backLogoPath);
            return defaultTemplate;
        }
        
        return (backLogoPath.contains("/") || backLogoPath.contains("\\")) 
            ? backLogoPath 
            : templatePath + backLogoPath;
    }
    
    /**
     * 从HTTP URL中提取相对路径
     */
    private String extractRelativePath(String url) {
        int index = url.indexOf(UPLOAD_FILE_PREFIX) + UPLOAD_FILE_PREFIX.length();
        return url.substring(index);
    }
    
    /**
     * 准备替换文本
     */
    private Map<String, String> prepareReplacements(Cert cert, SysUserDTO user) {
        Map<String, String> replacements = new HashMap<>();

        // 添加用户信息
        replacements.put(USER_NAME_PLACEHOLDER, user.getRealName());

        // 添加证书信息
        replacements.put(CERT_TITLE_PLACEHOLDER, cert.getTitle());
        replacements.put(CERT_CODE_PLACEHOLDER, cert.getCode());

        // 添加日期信息
        SimpleDateFormat dateFormat = new SimpleDateFormat(CERT_DATE_FORMAT_PATTERN);
        String currentDate = dateFormat.format(new Date());
        replacements.put(ISSUE_DATE_PLACEHOLDER, currentDate);

        return replacements;
    }
    
    /**
     * 输入参数验证
     */
    private void validateInputParameters(Cert cert, SysUserDTO user) {
        if (cert == null) {
            throw new IllegalArgumentException("证书信息不能为空");
        }
        if (user == null) {
            throw new IllegalArgumentException("用户信息不能为空");
        }
        if (StringUtils.isEmpty(user.getRealName())) {
            throw new IllegalArgumentException("用户姓名不能为空");
        }
    }

    /**
     * 处理PPTX文件（修复资源管理）
     */
    private void processPPTX(String inputPath, String outputPath, Map<String, String> replacements) throws IOException {
        FileInputStream is = null;
        XMLSlideShow ppt = null;
        FileOutputStream os = null;
        
        try {
            // 读取PPTX文件
            is = new FileInputStream(inputPath);
            ppt = new XMLSlideShow(is);
            
            // 替换文本
            replaceTextInPresentation(ppt, replacements);
            
            // 保存修改后的PPTX文件
            os = new FileOutputStream(outputPath);
            ppt.write(os);
            
            log.info("PPTX文件处理完成: {} -> {}", inputPath, outputPath);
            
        } catch (IOException e) {
            log.error("处理PPTX文件失败: {} -> {}, 错误: {}", inputPath, outputPath, e.getMessage());
            throw e;
        } finally {
            // 确保资源被正确关闭
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    log.warn("关闭输出流失败: {}", e.getMessage());
                }
            }
            // XMLSlideShow不需要显式关闭，它会随着底层流的关闭而释放资源
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    log.warn("关闭输入流失败: {}", e.getMessage());
                }
            }
        }
    }
    
    /**
     * 替换演示文稿中的文本
     */
    private void replaceTextInPresentation(XMLSlideShow ppt, Map<String, String> replacements) {
        for (XSLFSlide slide : ppt.getSlides()) {
            for (XSLFShape shape : slide.getShapes()) {
                if (shape instanceof XSLFTextShape) {
                    XSLFTextShape textShape = (XSLFTextShape) shape;
                    for (XSLFTextParagraph paragraph : textShape.getTextParagraphs()) {
                        for (XSLFTextRun run : paragraph.getTextRuns()) {
                            String text = run.getText();
                            if (text != null) {
                                // 替换所有匹配的占位符
                                String newText = text;
                                for (Map.Entry<String, String> entry : replacements.entrySet()) {
                                    if (text.contains(entry.getKey())) {
                                        newText = newText.replace(entry.getKey(), entry.getValue());
                                    }
                                }
                                if (!newText.equals(text)) {
                                    run.setText(newText);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 将PPTX转换为PDF（改进版本）
     * @param pptxPath PPTX文件路径
     * @param pdfPath PDF输出路径
     */
    private void convertToPDF(String pptxPath, String pdfPath) {
        // 输入验证
        if (StringUtils.isEmpty(pptxPath) || StringUtils.isEmpty(pdfPath)) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        
        File pptxFile = new File(pptxPath);
        if (!pptxFile.exists()) {
            throw new IllegalArgumentException("PPTX文件不存在: " + pptxPath);
        }
        
        Process process = null;
        try {
            log.info("开始PDF转换: {} -> {}", pptxPath, pdfPath);
            
            // 使用LibreOffice进行转换
            ProcessBuilder pb = new ProcessBuilder(
                    "soffice", "--headless", "--convert-to", "pdf", "--outdir", 
                    new File(pdfPath).getParent(), pptxPath
            );
            
            // 设置工作目录和环境
            pb.redirectErrorStream(true);
            
            process = pb.start();
            
            // 使用常量定义的超时时间
            boolean finished = process.waitFor(PDF_CONVERSION_TIMEOUT_SECONDS, java.util.concurrent.TimeUnit.SECONDS);
            
            if (!finished) {
                log.error("PDF转换超时，强制终止进程");
                process.destroyForcibly();
                throw new RuntimeException("PDF转换超时，超过" + PDF_CONVERSION_TIMEOUT_SECONDS + "秒");
            }
            
            int exitCode = process.exitValue();
            if (exitCode != 0) {
                // 读取错误输出
                StringBuilder errorOutput = readProcessOutput(process);
                log.error("PDF转换失败，退出码: {}, 错误信息: {}", exitCode, errorOutput);
                throw new RuntimeException("PDF转换失败，退出码: " + exitCode + ", 错误: " + errorOutput);
            }
            
            // 验证输出文件是否生成
            File pdfFile = new File(pdfPath);
            if (!pdfFile.exists()) {
                throw new RuntimeException("PDF文件生成失败，输出文件不存在: " + pdfPath);
            }
            
            log.info("PDF转换成功: {} -> {}, 文件大小: {} bytes", pptxPath, pdfPath, pdfFile.length());
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("PDF转换被中断: {}", e.getMessage());
            throw new RuntimeException("PDF转换被中断: " + e.getMessage(), e);
        } catch (Exception e) {
            log.error("PDF转换失败: {}", e.getMessage(), e);
            throw new RuntimeException("PDF转换失败: " + e.getMessage(), e);
        } finally {
            // 确保进程被正确清理
            if (process != null && process.isAlive()) {
                process.destroyForcibly();
            }
        }
    }
    
    /**
     * 读取进程输出信息
     */
    private StringBuilder readProcessOutput(Process process) {
        StringBuilder output = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line).append("\n");
            }
        } catch (Exception e) {
            log.warn("读取进程输出失败: {}", e.getMessage());
        }
        return output;
    }
    
    /**
     * 异步生成证书
     * @param cert 证书模板信息
     * @param user 用户信息
     * @return 异步结果
     */
    @Async
    public CompletableFuture<String> generateCertificateAsync(Cert cert, SysUserDTO user) {
        try {
            String result = generateCertificate(cert, user);
            return CompletableFuture.completedFuture(result);
        } catch (Exception e) {
            log.error("异步生成证书失败: {}", e.getMessage(), e);
            CompletableFuture<String> future = new CompletableFuture<>();
            future.completeExceptionally(e);
            return future;
        }
    }
    
    /**
     * 生成缓存键
     */
    private String generateCacheKey(Cert cert, SysUserDTO user) {
        StringBuilder keyBuilder = new StringBuilder();
        keyBuilder.append(cert.getId())
                  .append("_")
                  .append(user.getId())
                  .append("_")
                  .append(cert.getTitle().hashCode())
                  .append("_")
                  .append(user.getRealName().hashCode());
        return keyBuilder.toString();
    }
    
    /**
     * 清理缓存
     */
    public void clearCache() {
        fileCache.clear();
        log.info("证书生成缓存已清理");
    }
    
    /**
     * 获取缓存大小
     */
    public int getCacheSize() {
        return fileCache.size();
    }


}